#define CVAR_NAME_MAX_LENGTH 255
#define CVAR_VALUE_MAX_LENGTH 255

// Returns a cvar Handle that can be used to restore cvars.
stock ArrayList SaveCvars(ArrayList cvarNames) {
  ArrayList storageList = new ArrayList();
  ArrayList cvarNameList = new ArrayList(CVAR_NAME_MAX_LENGTH);
  ArrayList cvarValueList = new ArrayList(CVAR_VALUE_MAX_LENGTH);

  char nameBuffer[CVAR_NAME_MAX_LENGTH];
  char valueBuffer[CVAR_VALUE_MAX_LENGTH];
  for (int i = 0; cvarNames != null && i < cvarNames.Length; i++) {
    cvarNames.GetString(i, nameBuffer, sizeof(nameBuffer));

    ConVar cvar = FindConVar(nameBuffer);
    if (cvar != null) {
      cvar.GetString(valueBuffer, sizeof(valueBuffer));
      cvarNameList.PushString(nameBuffer);
      cvarValueList.PushString(valueBuffer);
    }
  }

  storageList.Push(cvarNameList);
  storageList.Push(cvarValueList);
  return storageList;
}

// Restores cvars to their previous value using a return value of SaveCvars.
stock void RestoreCvars(ArrayList &cvarStorage, bool close = false) {
  ArrayList cvarNameList = view_as<ArrayList>(cvarStorage.Get(0));
  ArrayList cvarValueList = view_as<ArrayList>(cvarStorage.Get(1));

  char name[CVAR_NAME_MAX_LENGTH];
  char value[CVAR_VALUE_MAX_LENGTH];
  for (int i = 0; i < cvarNameList.Length; i++) {
    cvarNameList.GetString(i, name, sizeof(name));
    cvarValueList.GetString(i, value, sizeof(value));

    ConVar cvar = FindConVar(name);
    if (cvar != null) {
      cvar.SetString(value);
    }
  }

  if (close) {
    CloseCvarStorage(cvarStorage);
  }
}

// Closes a cvar storage object returned by SaveCvars.
stock void CloseCvarStorage(ArrayList &cvarStorage) {
  delete view_as<Handle>(cvarStorage.Get(0));
  delete view_as<Handle>(cvarStorage.Get(1));
  delete cvarStorage;
  cvarStorage = null;
}

// Returns the first "word" in a line, as seperated by whitespace.
stock bool __firstWord(const char[] line, char[] buffer, int len) {
  char[] lineBuffer = new char[strlen(line)];
  strcopy(lineBuffer, strlen(line), line);
  TrimString(lineBuffer);
  int splitIndex = StrContains(line, " ");
  if (splitIndex == -1)
    splitIndex = StrContains(line, "\t");

  if (splitIndex == -1) {
    Format(buffer, len, "");
    return false;
  }

  int destLen = splitIndex + 1;
  if (destLen > len)
    destLen = len;

  strcopy(buffer, destLen, lineBuffer);
  return true;
}

// Wrapper for SetCvars from an arbitrary file.
// Returns if successful.
stock ArrayList ExecuteAndSaveCvars(const char[] cfgFile) {
  char lineBuffer[CVAR_NAME_MAX_LENGTH + CVAR_VALUE_MAX_LENGTH];
  char nameBuffer[CVAR_NAME_MAX_LENGTH];

  char filePath[PLATFORM_MAX_PATH];
  Format(filePath, sizeof(filePath), "cfg/%s", cfgFile);

  File file = OpenFile(filePath, "r");
  if (file != null) {
    ArrayList nameList = new ArrayList(CVAR_NAME_MAX_LENGTH);

    while (!file.EndOfFile() && file.ReadLine(lineBuffer, sizeof(lineBuffer))) {
      if (__firstWord(lineBuffer, nameBuffer, sizeof(nameBuffer))) {
        TrimString(nameBuffer);
        nameList.PushString(nameBuffer);
      }
    }

    ArrayList ret = SaveCvars(nameList);
    ServerCommand("exec %s", cfgFile);
    delete nameList;
    delete file;
    return ret;
  } else {
    LogError("Failed to open file for reading: %s", filePath);
    return null;
  }
}
